using System;
using CodeConsult.Nordic.nRF8001;

namespace CodeConsult.Adafruit.BLE_UART
{
    internal class PipesManager
    {
        private enum PipeStoreLocation : byte
        {
            Invalid = 0x00,
            Local = 0x01,
            Remote = 0x02
        }

        private enum PipeType : ushort
        {
            TxBroadcast = 0x0001,
            Tx = 0x0002,
            TxAck = 0x0004,
            Rx = 0x0008,
            RxAck = 0x0010,
            TxReq = 0x0020,
            RxReq = 0x0040,
            Set = 0x0080,
            TxSign = 0x0100,
            RxSign = 0x0200,
            RxAckAuto = 0x0400
        }

        public enum PipeErrorCode : byte
        {
            AciStatusErrorCreditNotAvailable = 0x91
        }

        private class Pipe
        {
            private byte _id;

            public PipesManager.PipeType PipeType { get; }
            public PipesManager.PipeStoreLocation PipeStoreLocation { get; }

            public Pipe(byte pipe, PipesManager.PipeStoreLocation pipeStoreLocation, PipesManager.PipeType pipeType)
            {
                _id = pipe;
                PipeType = pipeType;
                PipeStoreLocation = pipeStoreLocation;
            }
        }

        private readonly Pipe[] _pipes;
        private static readonly byte[] PipesBitmapDefault =  new byte[8];
        private byte[] PipesOpenBitmap { get; set; } = PipesBitmapDefault;
        private byte[] PipesClosedBitmap { get; set; } = PipesBitmapDefault;

        public PipesManager()
        {
            var rawMappings = RfGoStudioServices.ServicePipeTypeMappings;

            _pipes = new Pipe[rawMappings.Length];

            byte pipe = 1;
            foreach (var rawMapping in rawMappings)
            {
                if (BitConverter.IsLittleEndian)
                {
                    Utils.Swap(rawMapping, 1, 2);
                }

                AddPipe(pipe, (PipeStoreLocation)rawMapping[0], (PipeType)BitConverter.ToUInt16(rawMapping, 1));
                pipe++;
            }
        }

        private Pipe this[byte pipe] => _pipes[pipe -1];

        public void ResetPipesAvailable()
        {
            UpdatePipesAvailable(PipesBitmapDefault, PipesBitmapDefault);
        }

        public void UpdatePipesAvailable(byte[] pipesOpen, byte[] pipesClosed)
        {
            PipesOpenBitmap = pipesOpen;
            PipesClosedBitmap = pipesClosed;
        }

        public bool IsPipeAvailable(byte pipe)
        {
            var pipeIndex = pipe / 8;
            return (PipesOpenBitmap[pipeIndex] & (0x01 << (byte)(pipe % 8))) != 0;
        }

        public bool IsPipeClosed(byte pipe)
        {
            var pipeIndex = pipe / 8;
            return (PipesClosedBitmap[pipeIndex] & (0x01 << (byte)(pipe % 8))) != 0;
        }

        public bool IsTxPipe(byte pipe)
        {
            return this[pipe].PipeType == PipeType.Tx || this[pipe].PipeType == PipeType.TxAck;
        }

        public bool HasLocalStoreLocatiom(byte pipe)
        {
            return this[pipe].PipeStoreLocation == PipeStoreLocation.Local;
        }

        private void AddPipe(byte pipe, PipeStoreLocation pipeStoreLocation, PipeType pipeType)
        {
            _pipes[pipe - 1] = new Pipe(pipe, pipeStoreLocation, pipeType);
        }
    }
}
